﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ttbit.form;

namespace ttbit.form
{
    public class FontData
    {
        public Header header;
        public Dictionary<UInt32, Item> fontItems;
        public void Write(BinaryWriter stream)
        {
            header.charCount = (uint)fontItems.Count;
            header.Write(stream);
            foreach (var item in fontItems.Values)
            {
                item.Write(stream, header.fontBitmapSize);
            }
        }
        public void Read(BinaryReader stream)
        {
            header = new Header();
            header.Read(stream);
            fontItems = new Dictionary<UInt32, Item>();
            for (var i = 0; i < header.charCount; i++)
            {
                var item = new Item();
                item.Read(stream, header.fontBitmapSize);
                fontItems[item.charCode] = item;
            }
        }

        public void Load(System.IO.Stream stream)
        {
            var reader = new BinaryReader(stream);
            Read(reader);
        }
        public void Save(System.IO.Stream stream)
        {
            var writer = new BinaryWriter(stream);
            Write(writer);
        }
        public class Header
        {
            const string magic = "TTFontV1";
            public UInt32 charCount;
            public byte fontSize;
            public byte fontBorder;//可以加一个边，bit
            public uint fontBitmapSize
            {
                get
                {
                    return (uint)fontSize + fontBorder;
                }
            }
            public void Read(System.IO.BinaryReader stream)
            {
                byte[] buf = stream.ReadBytes(8);// (buf, 0, 8);
                var tm = System.Text.Encoding.ASCII.GetString(buf, 0, 8);
                if (tm != magic.Substring(0, 8))
                    throw new Exception("not ttfont file");
                charCount = stream.ReadUInt32();
                fontSize = stream.ReadByte();
                fontBorder = stream.ReadByte();
                if (charCount == 0 || charCount > 1000000)
                    throw new Exception("error char count.");
                if (fontSize < 8 || fontSize > 128)
                    throw new Exception("error fontSize");
                if (fontBorder > 128)
                    throw new Exception("error fontBorder");
            }
            public void Write(System.IO.BinaryWriter stream)
            {
                byte[] buf = System.Text.Encoding.ASCII.GetBytes(magic.Substring(0, 8));
                if (buf.Length != 8)
                    throw new Exception("error magic,must 8");
                stream.Write(buf, 0, 8);

                stream.Write(charCount);
                stream.Write(fontSize);
                stream.Write(fontBorder);
            }
        }
        public class Item
        {

            public UInt32 charCode;
            public byte width;
            public byte height;
            public sbyte emptyX;
            public sbyte emptyY;
            public byte advanceX;
            public byte[] graydata;//charwidth*charwidth
            public void Write(System.IO.BinaryWriter stream, uint bitmapfontsize)
            {
                if (graydata.Length != bitmapfontsize * bitmapfontsize)
                    throw new Exception("error graydata size");
                stream.Write(charCode);
                stream.Write(width);
                stream.Write(height);
                stream.Write(emptyX);
                stream.Write(emptyY);
                stream.Write(advanceX);
                stream.Write(graydata, 0, graydata.Length);
            }
            public void Read(System.IO.BinaryReader stream, uint bitmapfontsize)
            {
                charCode = stream.ReadUInt32();
                width = stream.ReadByte();
                height = stream.ReadByte();
                emptyX = stream.ReadSByte();
                emptyY = stream.ReadSByte();
                advanceX = stream.ReadByte();
                graydata = stream.ReadBytes((int)(bitmapfontsize * bitmapfontsize));
            }
        }
    }

}
public class TTFont
{
    FontData _data;
    private TTFont(FontData _data)
    {
        this._data = _data;
    }
    public string Name
    {
        get;
        private set;
    }
    public UInt32 charCount
    {
        get
        {
            return _data.header.charCount;
        }
    }
    public byte fontSize
    {
        get
        {
            return (byte)_data.header.fontSize;
        }
    }
    public byte fontBorder//可以加一个边，bit
    {
        get
        {
            return (_data.header.fontBorder);
        }
    }
    public uint fontBitmapSize
    {
        get
        {
            return (uint)fontSize + fontBorder;
        }
    }
    public bool HasChar(uint charcode)
    {
        return _data.fontItems.ContainsKey(charcode);
    }
    Dictionary<uint, TTFontItem> items = new Dictionary<uint, TTFontItem>();
    public TTFontItem GetChar(uint charcode)
    {
        if (items.TryGetValue(charcode, out TTFontItem item))
            return item;
        else if (HasChar(charcode))
        {
            var nitem = new TTFontItem(_data.fontItems[charcode]);
            items[charcode] = nitem;
            return nitem;
        }
        else
        {
            return null;
        }
    }
    public static TTFont FromAssetByName(string name)
    {
        var item = Env.RootPack.GetPackItem(name);
        if (item != null)
        {
            FontData data = new FontData();
            using var ms = new MemoryStream(item.Bytes);
            using var br = new BinaryReader(ms);
            data.Read(br);
            TTFont font = new TTFont(data);
            font.Name = item.Name;
            return font;
        }
        return null;
    }
    public static TTFont FromAsset(JSPackItem item)
    {
        if (item.Type == ".ttfont")
        {
            FontData data = new FontData();
            using var ms = new MemoryStream(item._item.Bytes);
            using var br = new BinaryReader(ms);
            data.Read(br);
            TTFont font = new TTFont(data);
            font.Name = item.Name;
            return font;
        }
        else
        {
            return null;
        }
    }
}
public class TTFontItem
{
    public UInt32 charCode;
    public byte width;
    public byte height;
    public sbyte emptyX;
    public sbyte emptyY;
    public byte advanceX;
    public byte[] graydata;//charwidth*charwidth
    internal TTFontItem(FontData.Item item)
    {
        this.charCode = item.charCode;
        this.width = item.width;
        this.height = item.height;
        this.emptyX = item.emptyX;
        this.emptyY = item.emptyY;
        this.advanceX = item.advanceX;
        this.graydata = item.graydata;
    }

}

